package cn.zhejianglao.shirotemplate.service.file.service;

import cn.zhejianglao.shirotemplate.common.model.FileVO;
import cn.zhejianglao.shirotemplate.common.model.PageRequest;
import cn.zhejianglao.shirotemplate.common.model.PagedResult;
import cn.zhejianglao.shirotemplate.exception.BusinessException;
import cn.zhejianglao.shirotemplate.exception.ExceptionCode;
import cn.zhejianglao.shirotemplate.service.file.dao.IFileMapper;
import cn.zhejianglao.shirotemplate.service.user.service.IUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.List;

@Slf4j
@Service
public class FileService implements IFileService {

    @Value("${upload.base.path}")
    private String uploadBasePath;

    @Autowired
    private IFileMapper fileMapper;

    @Autowired
    private IUserService userService;

    @Transactional
    @Override
    public void upload(MultipartFile file, String path) {
        if (file == null) {
            throw new BusinessException(
                    ExceptionCode.Param.NULL_PARAM_CODE,
                    ExceptionCode.Param.NULL_PARAM_MSG
            );
        }
        if (file.getOriginalFilename() != null && file.getOriginalFilename().length() > 100) {
            throw new BusinessException(
                    ExceptionCode.File.FILE_NAME_TOO_LONG_CODE,
                    ExceptionCode.File.FILE_NAME_TOO_LONG_MSG
            );
        }
        try {
            File localFile = getFile(file, path);
            file.transferTo(localFile);
            FileVO fileVO = new FileVO();
            Long currentUserId = userService.getCurrentUser().getId();
            fileVO.setCreatedBy(currentUserId);
            fileVO.setOwnerId(currentUserId);
            fileVO.setFileName(file.getOriginalFilename());
            fileVO.setPath(path);
            fileVO.setFileSize(file.getSize());
            fileMapper.insert(fileVO);
        } catch (IOException e) {
            throw new BusinessException(
                    ExceptionCode.IO.FAIL_TO_UPLOAD_FILE_CODE,
                    ExceptionCode.IO.FAIL_TO_UPLOAD_FILE_MSG
            );
        }
    }

    /**
     * 获取文件对象
     *
     * @param file 文件
     * @param path 路径
     * @return 文件对象
     */
    private File getFile(MultipartFile file, String path) {
        String uploadPath = uploadBasePath + File.separator + path;
        File uploadDir = new File(uploadPath);
        if (!uploadDir.exists() && !uploadDir.mkdir()) {
            throw new BusinessException(
                    ExceptionCode.IO.FAIL_TO_MKDIR_CODE,
                    ExceptionCode.IO.FAIL_TO_MKDIR_MSG
            );
        }
        String absFullUploadPath = uploadDir.getAbsolutePath() + File.separator + file.getOriginalFilename();
        return new File(absFullUploadPath);
    }

    @Override
    public void download(String path, String filename, HttpServletResponse response) {
        validateFileExistenceAndGetOwnerId(path, filename);
        response.reset();
        response.setContentType("application/octet-stream");
        response.setHeader("Content-disposition", "attachment;filename=" + filename);
        // 从文件读到servlet response输出流中
        File file = new File(uploadBasePath + File.separator + path + File.separator + filename);
        try (FileInputStream inputStream = new FileInputStream(file)) {  // try-with-resources
            byte[] b = new byte[1024];
            int len;
            while ((len = inputStream.read(b)) > 0) {
                response.getOutputStream().write(b, 0, len);
            }
        } catch (IOException e) {
            throw new BusinessException(
                    ExceptionCode.IO.FAIL_TO_UPLOAD_FILE_CODE,
                    ExceptionCode.IO.FAIL_TO_UPLOAD_FILE_MSG
            );
        }
    }

    @Transactional
    @Override
    public boolean delete(String path, String filename) {
        Long ownerId = validateFileExistenceAndGetOwnerId(path, filename);
        Long currentUserId = userService.getCurrentUser().getId();
        if (!ownerId.equals(currentUserId)) {
            throw new BusinessException(
                    ExceptionCode.Permission.INSUFFICIENT_PERMISSION_CODE,
                    ExceptionCode.Permission.INSUFFICIENT_PERMISSION_MSG
            );
        }
        File file = new File(uploadBasePath + File.separator + path + File.separator + filename);
        boolean result = file.delete();
        fileMapper.deleteByPathAndName(path, filename);
        return result;
    }

    @Override
    public PagedResult<FileVO> list(int size, int page) {
        PageRequest pageRequest = new PageRequest(size, page);
        Long currentUserId = userService.getCurrentUser().getId();
        List<FileVO> data = fileMapper.selectByPage(pageRequest, currentUserId);
        return new PagedResult<>(data, pageRequest);
    }

    /**
     * 判断文件是否存在并返回其owner id
     *
     * @param path 路径
     * @param filename 文件名
     * @return owner id
     */
    private Long validateFileExistenceAndGetOwnerId(String path, String filename) {
        File file = new File(uploadBasePath + File.separator + path + File.separator + filename);
        if (!file.exists()) {
            throw new BusinessException(
                    ExceptionCode.File.FILE_NOT_EXISTS_CODE,
                    ExceptionCode.File.FILE_NOT_EXISTS_MSG
            );
        }
        Long ownerId = fileMapper.selectOwnerIdByPathAndFilename(path, filename);
        if (ownerId == null) {
            throw new BusinessException(
                    ExceptionCode.File.FILE_NOT_EXISTS_CODE,
                    ExceptionCode.File.FILE_NOT_EXISTS_MSG
            );
        }
        return ownerId;
    }
}
